package start.spring.basic.filter;

import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Mono;
import start.spring.basic.util.GatewayExceptionUtil;
import start.spring.basic.util.common.StringUtil;

@Slf4j
@Order(-1000)
@RequiredArgsConstructor
@Component
public class GatewayExceptionHandler extends BaseFilter implements ErrorWebExceptionHandler {

	private final ObjectMapper objectMapper;

    @Override
    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
    	
    	ServerHttpRequest request = exchange.getRequest();
        
        String traceId = request.getHeaders().getFirst(super.REQUEST_KEY_TRACEID);
    	
        if(traceId==null) {
        	traceId = StringUtil.getUuid();
        }
        
        ServerHttpResponse response = exchange.getResponse();
        
        log.info("[{}]:error msg: {}", traceId, ex.getMessage());

        if (response.isCommitted()) {
            return Mono.error(ex);
        }

        // header set
        response.getHeaders().add(super.REQUEST_KEY_TRACEID, traceId);
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        if (ex instanceof ResponseStatusException) {
            response.setStatusCode(HttpStatus.OK);
        }

        JSONObject json = new JSONObject();
    	json.put("errcode", "gw500");
    	json.put("errmsg", GatewayExceptionUtil.getExceptionDeteil(ex, 0));
    	
    	log.info("[{}]:response data: {}", traceId, json.toJSONString());
    	
        return response
                .writeWith(Mono.fromSupplier(() -> {
                    DataBufferFactory bufferFactory = response.bufferFactory();
                    try {
                        return bufferFactory.wrap(objectMapper.writeValueAsBytes(json));
                    } catch (JsonProcessingException e) {
                        log.warn("Error writing response", ex);
                        return bufferFactory.wrap(new byte[0]);
                    }
                }));
    }
}
